package cn.bigfire.wedesk.net.protocol;


import cn.bigfire.wedesk.bean.Wedesk;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 类型type号，和业务关系不大。所以能自动绑定会更好一些。
 * 仅Protobuf格式的时候才需要用到这个TypeMapper
 */
public class TypeMapper {

    public static HashMap<Integer, Class<?>> type2ClassMap = new HashMap<>();//<Type(对于的类型号),Class(Bean的class)>
    public static HashMap<Class<?>, Integer> class2TypeMap = new HashMap<>();//<Class(PB+Bean的class),Type(对于的类型号)>

    static {
        try {
            //先把json放进去
            type2ClassMap.put(Wedesk.Type.TYPE_JSON_OBJECT.getValue(), JSONObject.class);
            type2ClassMap.put(Wedesk.Type.TYPE_JSON_ARRAY.getValue(), JSONArray.class);
            class2TypeMap.put(JSONObject.class, Wedesk.Type.TYPE_JSON_OBJECT.getValue());
            class2TypeMap.put(JSONArray.class, Wedesk.Type.TYPE_JSON_ARRAY.getValue());
            registerTypeAuto();//自动关联  约定大于配置 要求枚举Type:Type_+类名

            //如果自动关联不生效时，可以手动注入，格式如下
//        registerPbAndBean(Wedesk.Type.TYPE_Err_VALUE, Wedesk.Err.class, com.protobuf.bean.Wedesk.Err.class);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关联 PB类-Bean类-type号
     *
     * @param pbClass   pbClass
     * @param beanClass beanClass
     * @param type      类型号
     */
    public static void registerPbAndBean(int type, Class<?> pbClass, Class<?> beanClass) {
        if (!type2ClassMap.containsKey(type)) {
            class2TypeMap.put(pbClass, type);//<Class(PB+Bean的class),Type(对于的类型号)>      作用:根据pb找type        encode时自动填充type
            class2TypeMap.put(beanClass, type);//<Class(PB+Bean的class),Type(对于的类型号)>    作用:根据bean找type      encode时自动填充type
            type2ClassMap.put(type, beanClass);//<Type(对于的类型号),Class(Bean的class)>       作用:根据type转为bean类   decode时根据 type 把byte[]转javabean
        } else {
            System.err.printf("type(%s) ERROR,类型重复,已存在PB:[%s],Bean:[%s]%n", type, pbClass.getSimpleName(), beanClass.getSimpleName());
        }
    }

    /**
     * 自动关联
     * 关联规则：
     * 1.根据 TYPE_+类名 匹配type进行填充
     * 2.根据 自动找bean包下同名的类进行注入
     */
    public static void registerTypeAuto() {
        try {
            Class<?>[] protoClasses = Wedesk.class.getClasses();
            for (Class<?> protoClass : protoClasses) {
                if (protoClass.isEnum() || protoClass.getSimpleName().endsWith("OrBuilder")) {
                    continue;
                }
                Wedesk.Type type = tryGetType(protoClass);
                if (type != null) {
                    try {
                        Class<?> beanClass = Class.forName(protoClass.getName().replace("pb", "bean"));
                        TypeMapper.registerPbAndBean(type.getValue(), protoClass, beanClass);
                    } catch (Exception e) {
//                        e.printStackTrace();
                        System.err.println(protoClass.getSimpleName() + "没有匹配到有效的javabean");
                    }
                } else {
                    System.err.println(protoClass.getSimpleName() + "没有匹配到有效的枚举值");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据proto类名找匹配的Type
     *
     * @param protoClass proto类Class
     * @return Wedesk.Type 类型枚举
     */
    private static Wedesk.Type tryGetType(Class<?> protoClass) {
        try {
            String prefix = "TYPE_";
            List<String> nameList = new ArrayList<>();
            String simpleName = protoClass.getSimpleName();
            String underlineCase = StrUtil.toUnderlineCase(simpleName);
            String upperCase = underlineCase.toUpperCase();

            nameList.add(prefix + simpleName);//TYPE_+类名
            nameList.add(prefix + underlineCase);//TYPE_+类名+下划线
            nameList.add(prefix + upperCase);//TYPE_+类名+下划线+全大写
            for (String name : nameList) {
                try {
                    return Wedesk.Type.valueOf(name);//尝试3种格式的name，匹配到就返回。TYPE_+类名
                } catch (Exception e) {
//                    e.printStackTrace();//跳过当前name 尝试匹配下一个
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static int getType(Class<?> clazz) {
        return class2TypeMap.getOrDefault(clazz, 0);
    }

    public static Class<?> getCodec(int type) {
        return type2ClassMap.get(type);
    }

}
